package com.time.timespace.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.time.timespace.commons.constant.BaseConstants;
import com.time.timespace.commons.dto.ResponseEntity;
import com.time.timespace.commons.utils.RedisCacheDTO;
import com.time.timespace.domain.dto.TokenDTO;
import com.time.timespace.domain.dto.UserDetailsDTO;
import com.time.timespace.domain.entity.Charge;
import com.time.timespace.domain.entity.User;
import com.time.timespace.mapper.ChargeMapper;
import com.time.timespace.mapper.UserMapper;
import com.time.timespace.service.LoginService;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class LoginServiceImpl implements LoginService {

    public static final Logger LOGGER = LoggerFactory.getLogger(LoginServiceImpl.class);

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private RedisCacheDTO redisCache;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ChargeMapper chargeMapper;
    @Value("${spring.custom.token.expires_in}")
    private Integer EXPIRES_IN;

    @Override
    public TokenDTO login(User user) {
        LOGGER.info("登录的用户为：{}", user);
        //使用ProviderManager的AuthenticationManager的authenticate方法进行验证
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);

        //如果校验失败了
        if (Objects.isNull(authenticate)) {
            throw new RuntimeException("用户名或密码错误！");
        }

        //生成token给前端
        //判断user是否已经有了token信息
        TokenDTO token = redisCache.getCacheObject(user.getUserName());
        LOGGER.info("token:{}", token);
        if (Objects.nonNull(token)) {
            token.setExpires_in(redisCache.getExpire(user.getUserName()));
            return token;
        }

        String uuid = UUID.randomUUID().toString();
        TokenDTO tokenDTO = TokenDTO.builder()
                .access_token(uuid)
                .expires_in(Long.valueOf(EXPIRES_IN))
                .token_type(BaseConstants.BEARER)
                .build();
        //判断是否存在，并存入存入redis
        UserDetailsDTO cacheObject = redisCache.getCacheObject(uuid);
        UserDetailsDTO userDetailsDTO = (UserDetailsDTO) authenticate.getPrincipal();
        if (Objects.isNull(cacheObject)) {
            redisCache.setCacheObject(uuid, userDetailsDTO, EXPIRES_IN, TimeUnit.SECONDS);
            //存入userName和uuid的token的映射
            redisCache.setCacheObject(user.getUserName(), tokenDTO, EXPIRES_IN, TimeUnit.SECONDS);
        }

        return tokenDTO;
    }

    @Override
    public ResponseEntity<?> logout(HttpServletRequest request) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LOGGER.info("principal=>{}", authentication.getPrincipal());
        if ("anonymousUser".equals(authentication.getPrincipal())) {
            return new ResponseEntity<>(System.currentTimeMillis(), "500", "当前是匿名用户，无需退出", authentication.getPrincipal());
        }
        UserDetailsDTO userDetailDTO = (UserDetailsDTO) authentication.getPrincipal();
        String userName = userDetailDTO.getUser().getUserName();
        String token = TokenDTO.getToken(request);
        redisCache.deleteObject(token);
        redisCache.deleteObject(userName);
        return new ResponseEntity<>(System.currentTimeMillis(),"200", "退出成功", userDetailDTO);
    }

    @Override
    public User getSelfInfo(Long userId) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUserId, userId);
        User user = userMapper.selectOne(wrapper);
        List<Charge> chargeList = chargeMapper.selectChargeById(userId);
        BigDecimal inChargeAmount = BigDecimal.ZERO;
        BigDecimal outChargeAmount = BigDecimal.ZERO;
        if (CollectionUtils.isNotEmpty(chargeList)) {
            Map<String, List<Charge>> chargeMap = chargeMapper.selectChargeById(userId).stream().collect(Collectors.groupingBy(Charge::getType));
            List<Charge> inChargeList = chargeMap.get("in");
            List<Charge> outChargeList = chargeMap.get("out");
            if (CollectionUtils.isNotEmpty(inChargeList)) {
                inChargeAmount = inChargeList.stream().map(Charge::getAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            }
            if (CollectionUtils.isNotEmpty(outChargeList)) {
                outChargeAmount = outChargeList.stream().map(Charge::getAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            }
        }
        user.setInChargeAmount(inChargeAmount);
        user.setOutChargeAmount(outChargeAmount);
        return user;
    }

    @Override
    public User getSelfByName(String userName) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUserName, userName);
        User user = userMapper.selectOne(wrapper);
        return user;
    }

    @Override
    public void selfUpdate(User user) {
        if (Objects.nonNull(user.getUserId())) {
            // TODO: 有点危险，可以自己写SQL，防止其他字段被更新
            userMapper.updateById(user);
        }
    }
}
